﻿package src.computation 
{	
	/**
	* 
	* Reliability project
	* -----------------------
	* @author  Dmitrij Boycov
	* @mail    dmitry.boycov@gmail.com
	* @date    April 27, 2010
	* @version 1
	* 
	*/
	
	import flash.utils.Dictionary;	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	//
	import src.computation.Simulation
	import src.computation.CElement;
	import src.entity.Element
	import src.entity.Node;	
	import src.computation.TimeLine;
	import src.computation.Method;
	import src.util.Utils;
	import src.common.MsgEvent;
	
	public class SimulationWithRecovery extends Simulation
	{
		
		public function SimulationWithRecovery() {			
		}
		
		
		private function sortElementsByTime():Array {
			var keys = Utils.dictKeys(this.elements);
			var bul = true;
			while (bul) {
				bul = false;
				for (var i = 1; i < keys.length; i++) {
					var el1 = this.elements[keys[i]];
					var el2 = this.elements[keys[i - 1]];
					if (el1 != null && el2 != null) {
						if (el1.getTimeOfOperation() < el2.getTimeOfOperation()) {
							bul = true;
							var buf = keys[i];
							keys[i] = keys[i - 1];
							keys[i - 1] = buf;
						}
					}
				} 
			}
			return keys;
		}
		
		private function getElementWithSmallestTime() {
			var keys = Utils.dictKeys(this.elements);
			if (keys.length == 1) {
				return this.elements[keys[0]]
			}
			if(keys.length>1){
				var min = this.elements[keys[0]];
				for (var i = 1; i < keys.length; i++) {
					var el1 = this.elements[keys[i]];
					if (el1.getTimeOfOperation() <  min.getTimeOfOperation()) {
						min = el1;
					}
				}
				return min;
			}
			return null;
		}
		
		private function initWorkTimeAndRecoveryTime() {
			// init
			for each(var el:CElement in this.elements) {
				el.setTime(this.time);
				el.setTimeLine(new TimeLine(this.time));				
			}
			var amount = 1;
			var doWork = true;
			while (amount != 0) {
				amount = 0;
				var el = this.getElementWithSmallestTime();
				if (!el.nowWork()) {
					if (el.work()) {
						amount++;
					}
				} else {
					if (this.stock!=null && this.stock.haveReserveElements(el.stockId,el.getTimeOfOperation())) {
						if (el.replacement()) {
							amount++;
							this.stock.elementToRepair(el.stockId,el.getTimeOfOperation());
						}
					}else {
						if (el.recovery()) {
							amount++;
						}
					}
				}
			}
		}
		
		/**
		 * 	unitSeriesElement
		 *  	+---+	    +---+         +---+ 
		 *     -|   |-------|   |-   =>  -|   |-
		 * 		+---+	    +---+         +---+
		 * 		 t1			  t2 	       min(t1;t2)
		 * @param	idBetweenLink
		 */
		protected override function unitSeriesElement(idBetweenLink) {
			var betweenLink:CLink = this.links[idBetweenLink];			
			if (betweenLink.end != null && betweenLink.start != null && 
				betweenLink.end["type"] == Element.TYPE && betweenLink.start["type"] == Element.TYPE) {
				var elOne:CElement = this.elements[betweenLink.start["id"]];
				var elTwo:CElement = this.elements[betweenLink.end["id"]];
				if (elOne != null && elTwo != null) {
					elOne.unitRecoveryTime(elTwo.getTimeLine());						
					var idNewLink;
					if (betweenLink.id == elTwo.inLink) {
						idNewLink = elTwo.outLink;
					}
					if (betweenLink.id == elTwo.outLink) {
						idNewLink = elTwo.inLink;
					}					
					elOne.unLink(betweenLink.id);
					elTwo.unLink(betweenLink.id);
					delete this.links[betweenLink.id];
					elTwo.unLink(idNewLink);
					elOne.addLink(this.links[idNewLink], this.links);
					this.links[idNewLink].removeEnd(elTwo);
					this.links[idNewLink].addEnd(elOne);
					delete this.elements[elTwo.id];
				}
			}
		}
		
		protected override function reduceParallElements() {			
			var el:CElement;
			for each(el in this.elements) {
				var el2:CElement = this.getParallEl(el);
				if (el2 != null && el2 != el ) {
					// unit					
					el.unitWorkTime(el2.getTimeLine());

					var inL:CLink = this.links[el2.inLink];
					var outL:CLink = this.links[el2.outLink];
					var n1 = inL.getOtherEnd(el2);
					if(n1["type"]==Node.TYPE){
						inL.removeEnd(this.nodes[n1["id"]]);
					}	
					var n2 = outL.getOtherEnd(el2);
					if (n2["type"] == Node.TYPE) {
						outL.removeEnd(this.nodes[n2["id"]]);
					}					
					inL.removeEnd(el2);
					outL.removeEnd(el2);
					delete this.links[inL.id];
					delete this.links[outL.id];
					delete this.elements[el2.id];
					//
					this.modified = true;
				}
			}
		}
		
		/**
		 *  transformTriangle
		 *			|						   |
		 * 	  +-----0-----+				  +----0----+	
		 * 	  |		u1	  |				  |	 u1	    |
		 * 	+---+		+---+			+---+     +---+  
		 * 	|e1 |		|e3 |     =>  	|e1'|	  |e3'|
		 * 	+---+		+---+			+---+	  +---+	
		 * 	  |	  +---+	  |				  |    		|
		 * u2 0---|e2 |---0 u3			  0 u2  	0 u3
		 * 	  |	  +---+	  |				  |         |	
		 * 
		 * @param	e1
		 * @param	e2
		 * @param	e3
		 * @param	u1
		 * @param	u2
		 * @param	u3
		 */
		protected override function transformTriangle(e1:CElement, e2:CElement, e3:CElement, u1:CNode, u2:CNode, u3:CNode) {
			if (e1 == null || e2 == null || e3 == null || u1 == null || u2 == null || u3 == null) return;			
			var t1 = e1.getTimeLine();			
			var t3 = e3.getTimeLine();
			var t2 = e2.getTimeLine();
			e2.unitRecoveryTime(t1);
			t2.unitRecoveryTime(t3);
			
			e1.unitWorkTime(t2);
			e3.unitWorkTime(e2.getTimeLine());
			
			u2.unLink(e2.inLink); u2.unLink(e2.outLink); 
			u3.unLink(e2.inLink); u3.unLink(e2.outLink);
			delete this.links[e2.outLink];
			delete this.links[e2.inLink];
			delete this.elements[e2.id];
			this.modified = true;
		}	
		
		public override function startComputation() {
			this.iterations = 1;
			var iter = Number(this.iterationNumberInput.text);
			iter = Math.round(iter);			
			if (iter != null && iter > 0) {
				this.iterations = iter;
			}
			if (this.nodes == null || this.elements == null || this.links == null) {				
				dispatchEvent(new MsgEvent(Method.ERROR, "Есть разрыв в схеме."));
				return false;
			}
			this.deleteFreeElements();			 
			var inNode = this.nodes[1];		
			if (Utils.sizeDict(inNode.links) > 0) {
				this.removeUnnecessaryNodes();
				this.initElements();
	//			this.initWorkTimeAndRecoveryTime();
	//			if(this.stock !=null){
	//				this.stock.print();
	//			}	
				var bufElements = this.cloneDict(this.elements);
				var bufNodes = this.cloneDict(this.nodes);
				var bufLinks = this.cloneDict(this.links);	
				// loop by iteration
				for (var i = 0; i < this.iterations;i++){
					this.modified = true;
					this.initWorkTimeAndRecoveryTime();
					// The base loop transform scheme to one element.
				//	this.printDict(this.elements);
					if (Utils.sizeDict(this.elements) > 1) {
						while (this.modified) {
							this.modified = false;
							this.reduceTriangle();
							this.reduceSeriesElemnts();
							this.reduceParallElements();
							this.removeUnnecessaryNodes();
						}
					}
					this.printDict(this.elements);
					if (Utils.sizeDict(this.elements) == 1) {
						var keys = Utils.dictKeys(this.elements);						
						this.systemWorkTimes.push(this.elements[keys[0]]);
					}else {
						dispatchEvent(new MsgEvent(Method.ERROR, "Невозможно сократить схему."));
						return; 
					}			
					this.elements = this.cloneDict(bufElements);
					this.nodes = this.cloneDict(bufNodes);
					this.links = this.cloneDict(bufLinks);	
				}
				dispatchEvent(new Event(Method.STOP_COMPUTATION));	
			}else { // inNode do not have link.				
				dispatchEvent(new MsgEvent(Method.ERROR,"Есть разрыв в схеме."));
			}
		}
		
		public override function getResult() {			
			if (this.systemWorkTimes.length != 0 ) {				
				var sumTime = 0;
				var minTime = 0;
				var maxTime = 0;
				var n0 = 0;
				var kg = 0;
				for (var i = 0; i < this.systemWorkTimes.length; i++ ) {
					trace("ti:" + ti);
					if(this.systemWorkTimes[i]!=null){
						var ti = this.systemWorkTimes[i].getFirstWorkTime()
						if (ti > maxTime) { maxTime = ti; }
						if ( minTime > ti || minTime == 0) { minTime = ti; }
						if (ti < this.time) { n0++; }
						sumTime += ti;
						kg += this.systemWorkTimes[i].getKG();
					}
				}
				if (this.iterations == 0) {
					this.iterations = 1;					
				}
				var vbr = (this.iterations - n0) / this.iterations;
				var mediumTime = sumTime / this.iterations;
				var res = "Среднее время работы:" + mediumTime; 
				res += "\n ------------------------ \n";
				res += "Максимальное время работы:" + maxTime;
				res += "\n ------------------------ \n";
				res += "Минимальное время работы:" + minTime;
				res += "\n ------------------------ \n";
				res += "Вероятность безотказной работы:" + vbr;
				res += "\n ------------------------ \n";
				res += "Коэффициент готовности:"+ kg/this.iterations;
				return res;
			}
			return null; 
		}
		
	}
	
}